---
category: Getting Started
title: Your First Scene
---

<Tip type="info">
  Understanding Svelte and Three.js is important when using Threlte. If you're new to Svelte, start
  with the [official tutorial](https://svelte.dev/tutorial). If you're new to Three.js, check the
  [official documentation](https://threejs.org) and guides.
</Tip>

## Structuring your app

First we create a new Svelte file, `App.svelte`, where we import [`<Canvas>`](/docs/reference/core/canvas).

```svelte title="App.svelte"
<script>
  import { Canvas } from '@threlte/core'
  import Scene from './Scene.svelte'
</script>

<Canvas>
  <Scene />
</Canvas>
```

The `<Canvas>` component is the root of a Threlte app: it creates a renderer and a default camera,
sets sensible defaults (like antialiasing and color management),
and provides Threlte's runtime context. To access this runtime context, we'll need to <a href="/docs/learn/basics/app-structure">create a separate component</a>
called `Scene.svelte` and including it in our `App.svelte` file.

## Creating objects

At this point we're just looking at a blank screen. Let's add a simple cube to it.

In `Scene.svelte`, we import the [`<T>` component](/docs/reference/core/t), which is
the **main building block** of your Threlte application. It's **a thin, generic wrapper for any Three.js class**.
Here we'll create a
[`THREE.Mesh`](https://threejs.org/docs/index.html#api/en/objects/Mesh) with
a [`THREE.BoxGeometry`](https://threejs.org/docs/index.html#api/en/geometries/BoxGeometry) and
a [`THREE.MeshBasicMaterial`](https://threejs.org/docs/index.html#api/en/materials/MeshBasicMaterial).

We should now see a white cube on a transparent background.

```svelte title="Scene.svelte" {1-8}+
<script>
  import { T } from '@threlte/core'
</script>

<T.Mesh>
  <T.BoxGeometry />
  <T.MeshBasicMaterial />
</T.Mesh>
```

<Example
  path="first-scene/step-1"
  hideCode
/>

<Tip type="info" title="attach">

Behind the scenes we're using the property `attach` available on `<T>` to **attach an object to a property of
its parent**. Binding geometries to the property `geometry` and materials to the property `material` is a common
pattern so Threlte takes care of it for you.

  <details>
	<summary>
      Learn more
    </summary>

    We're using the property `attach` available on `<T>` to
    **attach an object to a property of its parent**. In our case we're **attaching** the underlying Three.js
    object of `<T.BoxGeometry>` to the property `geometry` of the `<T.Mesh>` component. We're also attaching
    the underlying Three.js object of `<T.MeshBasicMaterial>` to the property `material` of the `<T.Mesh>` component.

    ```svelte title="Scene.svelte"
    <script>
      import { T } from '@threlte/core'
    </script>

    <T.Mesh>
      <T.BoxGeometry attach="geometry" />
      <T.MeshBasicMaterial attach="material" />
    </T.Mesh>
    ```

    Binding geometries to the property `geometry` and materials to the property `material` is a common
    pattern so Threlte will take care of it. It checks for the properties `isMaterial` and `isGeometry` on
    the underlying Three.js object and attaches it to the correct property.

  </details>
</Tip>

<details>
	<summary>
		Three.js equivalent
	</summary>

```typescript
// creating the objects
const geometry = new THREE.BoxGeometry()
const material = new THREE.MeshBasicMaterial()
const mesh = new THREE.Mesh()

// "attaching" the objects
mesh.geometry = geometry
mesh.material = material
```

</details>

## Modifying objects

That cube is still a bit boring. Let's give it some color,
scale it up, and move it slightly upward. We can do this by
passing props to `<T>`.

```svelte title="Scene.svelte" {5-7}m
<script>
  import { T } from '@threlte/core'
</script>

<T.Mesh position.y={1}>
  <T.BoxGeometry args={[1, 2, 1]} />
  <T.MeshBasicMaterial color="hotpink" />
</T.Mesh>
```

<Example
  path="first-scene/step-2"
  hideCode
/>

Threlte automatically generates props for `<T>` based on the underlying Three.js object. This means you can easily guess most `<T>` props
based on the <a href="https://threejs.org/docs/index.html#manual/en/introduction/Creating-a-scene">Three.js docs</a> for the class you are using.

<details>
  <summary>
    Three.js equivalent
  </summary>

```ts
const mesh = new THREE.Mesh()
const geometry = new THREE.BoxGeometry(1, 2, 1)
const material = new THREE.MeshBasicMaterial()
mesh.position.y = 1
material.color.set('hotpink')
```

</details>

The special **args** prop we use in `<T.BoxGeometry>` corresponds to the
object's constructor arguments. Props interpreted from the underlying Three.js
object are called **auto props**, like `color` in our `<T.MeshBasicMaterial>`.
Leveraging Threlte's **Pierced Props** you can directly assigned to attributes
of props like `position.y` in our `<T.Mesh>`.

<details>
	<summary>
		Learn more
	</summary>
	#### `args`

    In Three.js objects are classes that are instantiated. These classes can
    receive one-time constructor arguments (`new THREE.SphereGeometry(1, 32)`). In
    Threlte, constructor arguments are always passed as an array via the prop
    `args`. If `args` change later on, the object must naturally get reconstructed
    from scratch!

    #### Auto props

    For all other props, Threlte tries to automatically interpret props passed to `<T>` component.

    **Step 1.** *Find Properties* - First, Threlte will try to find the **property
    on the underlying Three.js object** based on the **name of the prop**. In our
    example, [`color` is a property of
    `THREE.MeshBasicMaterial`](https://threejs.org/docs/index.html#api/en/materials/MeshBasicMaterial.color).

    **Step 2.** *Try `set` Methods* - Next, Threlte will look for a `set` method
    on that property and use it to set the new value. In our example it will call
    `material.color.set('hotpink')` to set the color of our material.

    **Step 3.** *Try setting the property directly* - If there's no `set` method,
    it will try to set the property directly. In our example, this equated to
    `mesh.position.y = 1`.

    **Step 4.** *Check for array values* - When setting a property that accepts
    more than one value (such as a `THREE.Vector3`: `vec3.set(1, 2, 3)`), we can
    pass an array as a prop.

    **Step 5.** *Keep the prop type constant for the lifetime of the component* -
    If the prop value changes, Threlte will try to set the property again. If the
    type of the prop value changes, Threlte won't be able to reliably do that. For
    instance the type of the value of a variable that is used as a prop should not
    change from a single number to an array of numbers.

    #### Pierced props

    Because the property `position` of our `THREE.Mesh` is a `THREE.Vector3`, it
    also has `x`, `y` and `z` properties which we can set directly via
    dot-notation, we call this **Pierced Props**.

</details>

<Tip
  type="tip"
  title="Primitive values"
>
  From a performance perspective, it's often better to use pierced props because
  [primitive](https://developer.mozilla.org/en-US/docs/Glossary/Primitive) prop values can safely be
  compared for equality. This means that if the value of a prop doesn't change, Threlte will skip
  any updates to the underlying Three.js object.
</Tip>

<Tip
  type="warning"
  title="Constant prop types"
>
  The type of an inferred prop (or "auto prop") must be constant. This means that the type of a prop
  must not change for the lifetime of the component. For instance you can't use a variable as a prop
  that is an array of numbers and then later on change the value of that variable to a single
  number. This is considered a type change and therefore not allowed.
</Tip>

## Pointing the camera

We now want to view our cube with some perspective. To manipulate the default camera, let's add the following:

```svelte title="Scene.svelte" {5-11}+
<script>
  import { T } from '@threlte/core'
</script>

<T.PerspectiveCamera
  makeDefault
  position={[10, 10, 10]}
  oncreate={(ref) => {
    ref.lookAt(0, 1, 0)
  }}
/>

<T.Mesh position.y={1}>
  <T.BoxGeometry args={[1, 2, 1]} />
  <T.MeshBasicMaterial color="hotpink" />
</T.Mesh>
```

<Example
  path="first-scene/step-3"
  hideCode
/>

We're again using the `<T>` component to create a [`THREE.PerspectiveCamera`](https://threejs.org/docs/index.html#api/en/cameras/PerspectiveCamera).
We're also passing a `makeDefault` prop which will make this camera the default camera of our application.
The renderer now uses this camera to render our scene.

<Tip type="info" title="Events">

Threlte supports listening to certain <a href="/docs/reference/core/t#events">events on `<T/>` components</a>. Here, we use the `create`
event to get a reference to the underlying Three.js object as soon as it's created and use the method `lookAt` to look at the cube.

</Tip>

## Enabling interactivity

Let's say we want to scale our cube as soon as we hover over it. We first have to import the
[`interactivity`](/docs/reference/extras/interactivity) [plugin](/docs/learn/advanced/plugins) from
[`@threlte/extras`](/docs/reference/extras/getting-started) and invoke it in our `Scene.svelte` file.

The `interactivity` plugin lets us add interaction event listeners like `pointerenter` and
`pointerleave` to our `<T>` components. In these event handlers we'll update the value of a `Spring` from `svelte/motion`
and use its `.current` value to set the `scale` property of the `<T.Mesh>` component.

```svelte title="Scene.svelte" {3-4,6-7,20-26}+
<script>
  import { T } from '@threlte/core'
  import { interactivity } from '@threlte/extras'
  import { Spring } from 'svelte/motion'

  interactivity()
  const scale = new Spring(1)
</script>

<T.PerspectiveCamera
  makeDefault
  position={[10, 10, 10]}
  oncreate={(ref) => {
    ref.lookAt(0, 1, 0)
  }}
/>

<T.Mesh
  position.y={1}
  scale={scale.current}
  onpointerenter={() => {
    scale.target = 1.5
  }}
  onpointerleave={() => {
    scale.target = 1
  }}
>
  <T.BoxGeometry args={[1, 2, 1]} />
  <T.MeshBasicMaterial color="hotpink" />
</T.Mesh>
```

<Example
  path="first-scene/step-4"
  hideCode
/>

<Tip type="info" title="Automatic vector & scalar detection">
  You might have noticed that we're only passing a single number to the prop `scale` on `<T.Mesh>`. Threlte automatically
  figures out whether you are passing an array or a number and uses the appropriate underlying Three.js method.

  <details>
    <summary>
      Learn more
    </summary>
    The component `<T>` will first look for a property `setScalar` on	the underlying Three.js object and use that method if
    only a single number is passed. This is equivalent to calling `scale.setScalar(scale.current)`.
  </details>
</Tip>

<Tip
  type="tip"
  title="Realtime variables"
>
  When working with realtime apps where variables e.g. position and rotation change constantly, an
  easy way observe the values is with [live
  expressions](https://developer.chrome.com/docs/devtools/console/live-expressions/).
</Tip>

## Adding animation

Let's add some motion to our cube. We will use Threlte's [`useTask`](/docs/reference/core/use-task) hook to tap
into Threlte's **unified frame loop** and run a function on every frame. We again use a Pierced Prop to let the
cube rotate around its y-axis.

```svelte title="Scene.svelte" {2}m {10-13,25}+
<script>
  import { T, useTask } from '@threlte/core'
  import { interactivity } from '@threlte/extras'
  import { Spring } from 'svelte/motion'

  interactivity()

  const scale = new Spring(1)

  let rotation = $state(0)
  useTask((delta) => {
    rotation += delta
  })
</script>

<T.PerspectiveCamera
  makeDefault
  position={[10, 10, 10]}
  oncreate={(ref) => {
    ref.lookAt(0, 1, 0)
  }}
/>

<T.Mesh
  rotation.y={rotation}
  position.y={1}
  scale={scale.current}
  onpointerenter={() => {
    scale.target = 1.5
  }}
  onpointerleave={() => {
    scale.target = 1
  }}
>
  <T.BoxGeometry args={[1, 2, 1]} />
  <T.MeshBasicMaterial color="hotpink" />
</T.Mesh>
```

<Example
  path="first-scene/step-5"
  hideCode
/>

`useTask` registers a callback that will be invoked on every frame. The callback receives the time delta since the last frame as an argument. We use
the delta to update the rotation
[independent of the frame rate](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame)
– the cube will rotate at the same speed regardless of the frame rate.

## Adjusting the lighting

We're almost done. Let's add some shading to our cube and a light source. We'll use a
`THREE.MeshStandardMaterial` on our cube and a `THREE.DirectionalLight` to illuminate our scene.

```svelte title="Scene.svelte" {38}m {24}+
<script>
  import { T, useTask } from '@threlte/core'
  import { interactivity } from '@threlte/extras'
  import { Spring } from 'svelte/motion'

  interactivity()

  const scale = new Spring(1)

  let rotation = $state(0)
  useTask((delta) => {
    rotation += delta
  })
</script>

<T.PerspectiveCamera
  makeDefault
  position={[10, 10, 10]}
  oncreate={(ref) => {
    ref.lookAt(0, 1, 0)
  }}
/>

<T.DirectionalLight position={[0, 10, 10]} />

<T.Mesh
  rotation.y={rotation}
  position.y={1}
  scale={scale.current}
  onpointerenter={() => {
    scale.target = 1.5
  }}
  onpointerleave={() => {
    scale.target = 1
  }}
>
  <T.BoxGeometry args={[1, 2, 1]} />
  <T.MeshStandardMaterial color="hotpink" />
</T.Mesh>
```

<Example
  path="first-scene/step-6"
  hideCode
/>

## Casting shadows

We would like our cube to cast a shadow. To do so, we need a floor for it to cast a shadow _on_,
so we add a new `<T.Mesh>` but this time with `<T.CircleGeometry>`. To enable shadows, we need to
set `castShadow` on both the light and our cube, and set `receiveShadow` on our new floor:

```svelte title="Scene.svelte" {26,39,45-51}+
<script>
  import { T, useTask } from '@threlte/core'
  import { interactivity } from '@threlte/extras'
  import { Spring } from 'svelte/motion'

  interactivity()

  const scale = new Spring(1)

  let rotation = $state(0)
  useTask((delta) => {
    rotation += delta
  })
</script>

<T.PerspectiveCamera
  makeDefault
  position={[10, 10, 10]}
  oncreate={(ref) => {
    ref.lookAt(0, 1, 0)
  }}
/>

<T.DirectionalLight
  position={[0, 10, 10]}
  castShadow
/>

<T.Mesh
  rotation.y={rotation}
  position.y={1}
  scale={scale.current}
  onpointerenter={() => {
    scale.target = 1.5
  }}
  onpointerleave={() => {
    scale.target = 1
  }}
  castShadow
>
  <T.BoxGeometry args={[1, 2, 1]} />
  <T.MeshStandardMaterial color="hotpink" />
</T.Mesh>

<T.Mesh
  rotation.x={-Math.PI / 2}
  receiveShadow
>
  <T.CircleGeometry args={[4, 40]} />
  <T.MeshStandardMaterial color="white" />
</T.Mesh>
```

<Example
  path="first-scene/step-7"
  hideCode
/>

## Conclusion

Congratulations, you've just created your first Three.js scene with Threlte! It includes important
Three.js and Threlte concepts and should give you a good starting point for your first Threlte project.
